﻿/**
 * @file uart.c
 * @date 23.5.3
 * @author yrz
 * @brief uart相关寄存器操作
 * @version 0.1
 * @status Stable

**/

#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "riscv.h"
//#include "spinlock.h"
#include "proc.h"
#include "defs.h"


//返回寄存器编号所对应的uart寄存器地址
#define Reg(reg) ((volatile unsigned char *)(UART0 + reg))

//寄存器设置
#define RHR 0                 // 接收保持寄存器，用于输入字节
#define THR 0                 // 传输保持寄存器，用于输出字节
#define IER 1                 // 中断使能寄存器
#define IER_RX_ENABLE (1<<0)
#define IER_TX_ENABLE (1<<1)
#define FCR 2                 // FIFO控制寄存器
#define FCR_FIFO_ENABLE (1<<0)
#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
#define ISR 2                 // 中断状态寄存器
#define LCR 3                 // line control register
#define LCR_EIGHT_BITS (3<<0)
#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
#define LSR 5                 // line status register
#define LSR_RX_READY (1<<0)   // input is waiting to be read from RHR
#define LSR_TX_IDLE (1<<5)    // THR can accept another character to send

#define ReadReg(reg) (*(Reg(reg)))
#define WriteReg(reg, v) (*(Reg(reg)) = (v))

// the transmit output buffer.
//struct spinlock uart_tx_lock;
#define UART_TX_BUF_SIZE 32
char uart_tx_buf[UART_TX_BUF_SIZE];
uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE]
uint64 uart_tx_r; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]

extern volatile int panicked; // 来自 printf.c

void uartstart();


/**
 * @brief 初始化uart各个寄存器
*/
void
uartinit(void)
{
    // 关中断
    WriteReg(IER, 0x00);

    // 设置波特率状态
    WriteReg(LCR, LCR_BAUD_LATCH);

    
    //波特率38400的最低有效位
    WriteReg(0, 0x03);

    
    //波特率38400的最高有效位
    WriteReg(1, 0x00);

    
    
    //设置字符长度为8比特，
    WriteReg(LCR, LCR_EIGHT_BITS); // LCR: 0 0 0 0 0 0 1 1

    // 重置并使能FIFO队列.
    WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); // FCR: 0 0 0 0 0 1 1 1

    // 使能传输和接收中断
    WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);   // IER: 0 0 0 0 0 0 1 1

    //初始化锁
    //initlock(&uart_tx_lock, "uart");
}


/**
 * @brief 将字符写入输出缓冲区，通知UART开始发送字符。
 * @brief 若输出缓冲区满则阻塞。只适合在write调用中使用。
 * @param c 输入字符
*/
void
uartputc(int c)
{
    //acquire(&uart_tx_lock);

    if (panicked) {
        for (;;)
            ;
    }

    
    while (uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE) {
        // 缓冲区满
        // 休眠，等待uartstart()将其唤醒
        sleep(&uart_tx_r);     
    }
    uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
    uart_tx_w += 1;
    uartstart();
    //release(&uart_tx_lock);
}




/**
 * @brief uartputc()的变体，不需要使用中断。
 * @brief 内核的printf使用该函数输入字符，它会忙等uart的输出寄存器变为空。
 * @param c 输入字符
*/
void
uartputc_sync(int c)
{
    // push_off();

    if (panicked) {
        for (;;)
            ;
    }

    //循环等待，当THR传输保持寄存器空闲时，将c写入寄存器中
    while ((ReadReg(LSR) & LSR_TX_IDLE) == 0)
        ;
    WriteReg(THR, c);

    // pop_off();
}


/**
 * @brief 如果UART空闲且传输缓冲区中有字符等待，将字符输出。
 * @brief 调用该函数的进程必须持有uart_tx_lock互斥锁。可以通过top-half和bottom-half中断调用
 * @brief top-half：在关中断的条件下执行的,具有"原子"性,而且是中断发生以后一般要立即执行的。
 * @brief bottom-half：在开中断的条件下执行,可以延迟一段时间执行,可能将多个中断的bottom half合并起来一起执行
 
*/
void
uartstart()
{
    while (1) {
        if (uart_tx_w == uart_tx_r) {
            // 传输缓冲区为空则结束进程
            return;
        }

        if ((ReadReg(LSR) & LSR_TX_IDLE) == 0) {
            
            // 传输保持寄存器满，无法再写入字符。若其可以写新字符则会中断
            return;
        }

        // 将缓冲区字符写入c，并指针后移
        int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
        uart_tx_r += 1;

        /* 暂时采用忙等，不使用*/
        // 后移后产生空位，若有uartputc()等待空间写入，将其唤醒
        wakeup(&uart_tx_r);

        WriteReg(THR, c);
    }
}


/**
 * @brief 从UART读入一个输入字符
 * @return 若LSR（line status register）最低位为1，输出RHR寄存器中的值，否则输出-1。
 * @return 最低位表示receive data ready。
*/
int
uartgetc(void)
{
    if (ReadReg(LSR) & 0x01) {
        // 输入数据已准备就绪.
        return ReadReg(RHR);
    }
    else {
        return -1;
    }
}

/**
 * @brief uart中断处理，当输入数据抵达或uart准备好更多输出时调用，被devintr函数调用。
*/
void
uartintr(void)
{
    // 读并处理收到的字符.
    while (1) {
        int c = uartgetc();
        if (c == -1)
            break;
        consoleintr(c);
    }

    // 将缓冲区中的数据传到THR寄存器（FIFO控制寄存器），用于输出到命令行
    // acquire(&uart_tx_lock);
    uartstart();
    // release(&uart_tx_lock);
}
